<?php
class Dispatcher {
	
	public $url;
	
	public $controllerName;
	public $actionName;
	public $controllerParams;
	
	public $controller;

	//Extras: (não são modificados no formato padrão de url!)
	
	//se quiser passar alguma parte da url como parametro para o constructor...
	public $constrParams = array();
	
	// usar caminho a partir da pasta padrão dos controllers
	// ex: $controllerFolder = "cursos/geral/"
	// null = "ROOT/controllers/" (na raiz do diretório de controllers padrão)
	public $controllerSubFolder = null;
	
	//==========================================================================================	
	
	/**
	 * Concentra aqui toda a execução da classe:
	 * Chama todas as funções que serão executadas para o funcionamento do dispatcher
	 */
	public function __construct()
	{
		$this->url = $GLOBALS['_URL'];
		
		$this->config();					// carrega variaveis de configuração
		$this->requires();					// faz requires
		$this->loadController();			// realiza testes e carrega controller
		$this->controller->beforeAction();
		$this->loadAction();				// realiza testes e executa a action
		$this->controller->beforeRender();
		$this->controller->render();		//manda o controller se renderizar
		$this->controller->afterRender();	
	}
	
	/// CONFIGURAÇÕES: =============================================================================
	
	/**
	 * Define quem é quem.
	 * Modifique aqui caso queira um comportamento diferente para urls 
	 * ou cole dentro de um if caso seja somente para algum pedaço específico da url
	 */
	public function config()
	{	
		$this->controllerName 	= isset($this->url[1]) ? $this->url[1] : null;
		$this->actionName 		= isset($this->url[2]) ? $this->url[2] : null;
		
		//passa como parametro tudo que está a partir da terceira posição da url.
		$this->controllerParams = array_slice($this->url, 3);
	}
	
	/**
	 * Modifique aqui caso nescessite carregar mais arquivos (para herdar por exemplo)
	 */
	public function requires()
	{
		require ENGINE . "Controller.class.php";
		require APP_ROOT . "app_controller.php";
	}
	
	/**
	 * Ação executada caso o controle requerido não exista.
	 */
	public function onControllerNotFound()
	{
		//assume que ele tá chamando o index (e dá um unshift em geral)
		array_unshift($this->controllerParams, $this->actionName);
		$this->actionName = $this->controllerName;
		$this->controllerName = "index";
	}
	
	/**
	 * Ação executada caso a action requerida não exista.
	 */
	public function onActionNotFound()
	{
		array_unshift($this->controllerParams, $this->actionName);
		$this->actionName = "index";
	}
	
	
	/// NÃO MEXA A PARTIR DAQUI ==============================================================================
	
	/**
	 * Realiza testes e carrega o controller
	 */
	public function loadController()
	{
		$controllerPath = CONTROLLERS . $this->controllerSubFolder . $this->controllerName . "_controller.php";
		
		if (!file_exists($controllerPath)) 
		{
			$this->onControllerNotFound();
		}
		
		$controllerPath = CONTROLLERS . $this->controllerSubFolder . $this->controllerName . "_controller.php";
		
		if (!file_exists($controllerPath)) 
		{
			trigger_error("Controller $controllerPath inexistente!", E_USER_WARNING);
			error404();
		}
		
		require $controllerPath;
	
		$className = ucfirst($this->controllerName) . "Controller";
		
		if(!class_exists($className))
		{
			trigger_error("Nomenclatura inconsistente: classe controller nomeada incorretamente!! >.< ", E_USER_WARNING);
			error404();
		}
		
		
		//se tudo correu bem até aqui instancia o controller (passando $constrParams como parametros)
		$strConstrParams = array();
		foreach($this->constrParams as $key => $param) {
			$strConstrParams[] = "\$this->constrParams[$key]";
		}
		eval("\$this->controller = new $className(". implode(", ", $strConstrParams) .");");

		$this->controller->name = $this->controllerName;
		
		//carrega nele o nome da sub-pasta aonde supostamente estarão as views
		$this->controller->name 		= $this->controllerName;
		$this->controller->subfolder 	= $this->controllerSubFolder;
		
		$this->controller->_afterConstruction();
	}
	
	/**
	 * Realiza testes e chama a action
	 */
	public function loadAction()
	{
		// testa se existe a action, senão chama a index e reordena os parâmetros
		if (!method_exists($this->controller, $this->actionName)) {
			$this->onActionNotFound();
		}
		
		if (!method_exists($this->controller, $this->actionName)) {
			trigger_error("Action $this->actionName inexistente!", E_USER_WARNING);
			error404();
		}
		
		//carrega o nome que supostamente será o da view (igual ao nome da action)
		$this->controller->actionCalled = $this->actionName;
		$this->controller->_beforeActionCall();
		
		//chama a action...
		call_user_func_array(array($this->controller, $this->actionName), $this->controllerParams);
	}
}
?>